package com.kongjs.application.tenant;

import com.kongjs.application.cache.UserCacheManager;
import com.kongjs.application.cache.UserCacheManagerImpl;
import com.kongjs.application.model.dto.RoleCacheDTO;
import com.kongjs.application.model.dto.UserCacheDTO;
import com.kongjs.common.mybatis.tenant.TenantContextHolder;
import com.kongjs.common.mybatis.user.UserContextHolder;
import org.springframework.context.annotation.Primary;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.NumberUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

@Primary
@Component
public class TenantRoleHierarchy implements RoleHierarchy {
    private final Map<String, RoleHierarchy> roleHierarchyMap = new ConcurrentHashMap<>();

    public static String removePrefix(String str, String prefix) {
        if (str != null && str.startsWith(prefix)) {
            return str.substring(prefix.length());
        }
        return str;
    }

    @Override
    public Collection<? extends GrantedAuthority> getReachableGrantedAuthorities(Collection<? extends GrantedAuthority> authorities) {
        UserCacheManager userCacheManager = UserCacheManagerImpl.getInstance();
        Set<GrantedAuthority> grantedAuthorities = new HashSet<>();
        String userId = UserContextHolder.getUserId();
        String tenantId = TenantContextHolder.getTenantId();
        Long uid = null;
        Long tid = null;
        try {
            uid = NumberUtils.parseNumber(userId, Long.class);
            tid = NumberUtils.parseNumber(tenantId, Long.class);
        } catch (Exception ignored) {
        }
        if (ObjectUtils.isEmpty(uid) || ObjectUtils.isEmpty(tid)) {
            return authorities;
        }
        UserCacheDTO userCache = userCacheManager.loadUserCache(uid);
        if (ObjectUtils.isEmpty(userCache) || ObjectUtils.isEmpty(userCache.getEnabled()) || userCache.getEnabled().equals(0)) {
            return authorities;
        }
        Set<Long> roleIds = userCache.getRoleIds();
        if (CollectionUtils.isEmpty(roleIds)) {
            return authorities;
        }
        Map<Long, RoleCacheDTO> roleCacheMap = userCacheManager.loadRoleCache(roleIds);
        for (RoleCacheDTO roleCache : roleCacheMap.values()) {
            if (ObjectUtils.isEmpty(roleCache.getEnabled()) || roleCache.getEnabled().equals(0)) {
                continue;
            }
            grantedAuthorities.add(new SimpleGrantedAuthority(roleCache.getAuthority()));
        }
        if (StringUtils.hasText(tenantId)) {
            RoleHierarchy roleHierarchy = roleHierarchyMap.get(tenantId);
            if (!ObjectUtils.isEmpty(roleHierarchy)) {
                return roleHierarchy.getReachableGrantedAuthorities(grantedAuthorities);
            }
        }
        return grantedAuthorities.stream().map(grantedAuthority -> new SimpleGrantedAuthority(removePrefix(grantedAuthority.getAuthority(), "SCOPE_"))).collect(Collectors.toSet());
    }

    public void setRoleHierarchyMap(Map<String, RoleHierarchy> roleHierarchyMap) {
        this.roleHierarchyMap.clear();
        this.roleHierarchyMap.putAll(roleHierarchyMap);
    }

    public void saveRoleHierarchy(String tenantId, RoleHierarchy roleHierarchy) {
        this.roleHierarchyMap.put(tenantId, roleHierarchy);
    }

    public void removeRoleHierarchy(String tenantId) {
        this.roleHierarchyMap.remove(tenantId);
    }
}
